package cn.lokei.task.config.database;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean;

import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.util.CollectionUtils;

@Slf4j
@Configuration
@PropertySource(value = { "file:conf/db.properties" })
// 扫描 Mapper 接口并容器管理
@MapperScan(basePackages = DataSourceConfig.PACKAGE, sqlSessionFactoryRef = "sqlSessionFactory")
public class DataSourceConfig {

	@Autowired
	private RedisTemplate<String, Object> redisTemplate;

	// 精确到 db 目录，以便跟其他数据源隔离
	static final String PACKAGE = "cn.lokei.task.dao";
	static final String MAPPER_LOCATION = "classpath*:mapper/*.xml";

	@Value("${db.datasource.url}")
	private String url;

	@Value("${db.datasource.username}")
	private String user;

	@Value("${db.datasource.password}")
	private String password;

	@Value("${db.datasource.driver-class-name}")
	private String driverClass;

	@Bean(name = "dbDataSource")
	public DataSource dbDataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driverClass);
		dataSource.setUrl(url);
		dataSource.setUsername(user);
		dataSource.setPassword(password);
		return dataSource;
	}

	// @Bean(name = "dbTransactionManager")
	// public DataSourceTransactionManager dbTransactionManager() {
	// return new DataSourceTransactionManager(dbDataSource());
	// }

	// @Bean(name = "sqlSessionFactory")
	// public SqlSessionFactory sqlSessionFactory(@Qualifier("dbDataSource")
	// DataSource dbDataSource)
	// throws Exception {
	// final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
	// sessionFactory.setDataSource(dbDataSource);
	// sessionFactory.setMapperLocations(
	// new
	// PathMatchingResourcePatternResolver().getResources(dbDataSourceConfig.MAPPER_LOCATION));
	// return sessionFactory.getObject();
	// }

	@Bean(name = "dynamicDataSource")
	@Qualifier("dynamicDataSource")
	public DynamicRoutingDataSource dynamicDataSource() throws SQLException {
		DynamicRoutingDataSource dynamicDataSource = new DynamicRoutingDataSource();
		Set<Object> proj_set = redisTemplate.opsForSet().members("proj:saas");
		if (proj_set != null && proj_set.size() > 0) {
			Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
			dynamicDataSource.setTargetDataSources(targetDataSources);
			boolean to_be_default = true;
			for (Object project : proj_set) {
				JSONObject proj_info = JSON.parseObject(project.toString());
				if (proj_info.getString("type").equals("5-1")) {
					DatabaseInfo databaseInfo = new DatabaseInfo("saas" + proj_info.getString("open_mode"), user, password);
					dynamicDataSource.checkCreateDataSource(databaseInfo);
					if (to_be_default) {
						// 配置缺省的数据源
						if (dynamicDataSource.getResolvedDataSources().get(databaseInfo.getFactoryCode()) != null) {
							dynamicDataSource.setDefaultTargetDataSource(
									dynamicDataSource.getResolvedDataSources().get(databaseInfo.getFactoryCode()));
							to_be_default = false;
						}
					}
				} else {
					DatabaseInfo databaseInfo = new DatabaseInfo(proj_info.getString("pid"), user, password);
					dynamicDataSource.checkCreateDataSource(databaseInfo);
					if (to_be_default) {
						// 配置缺省的数据源
						if (dynamicDataSource.getResolvedDataSources().get(databaseInfo.getFactoryCode()) != null) {
							dynamicDataSource.setDefaultTargetDataSource(
									dynamicDataSource.getResolvedDataSources().get(databaseInfo.getFactoryCode()));
							to_be_default = false;
						}
					}
				}
			}
		} else {
			// 配置缺省的数据源
			dynamicDataSource.setDefaultTargetDataSource(dbDataSource());
			Map<Object, Object> targetDataSources = new HashMap<Object, Object>();
			// 额外数据源配置 TargetDataSources
			// targetDataSources.put("mainDataSource", dataSource());
			dynamicDataSource.setTargetDataSources(targetDataSources);
		}
		return dynamicDataSource;
	}

	@Bean(name = "sqlSessionFactory")
	public SqlSessionFactory sqlSessionFactory() throws Exception {
		MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
		sqlSessionFactoryBean.setDataSource(dynamicDataSource());
		// 对新的SqlSessionFactory配置 修改mybatis-plus Page自动分页失效问题 以及 找不到xml问题
		// MybatisConfiguration configuration = new MybatisConfiguration();
		// configuration.addInterceptor(new
		// MybatisPlusConfig().paginationInterceptor());
		// sqlSessionFactoryBean.setConfiguration(configuration);
		sqlSessionFactoryBean.setMapperLocations(resolveMapperLocations());
		return sqlSessionFactoryBean.getObject();
	}

	public Resource[] resolveMapperLocations() {
		ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver();
		List<String> mapperLocations = new ArrayList<>();
		mapperLocations.add("classpath*:mapper/*.xml");
		mapperLocations.add("classpath*:mapper/*/*.xml");
		List<Resource> resources = new ArrayList<Resource>();
		if (!CollectionUtils.isEmpty(mapperLocations)) {
			for (String mapperLocation : mapperLocations) {
				try {
					Resource[] mappers = resourceResolver.getResources(mapperLocation);
					resources.addAll(Arrays.asList(mappers));
				} catch (IOException e) {
					log.error("Get myBatis resources happened exception", e);
				}
			}
		}

		return resources.toArray(new Resource[resources.size()]);
	}

	/**
	 * @Description: 将动态数据加载类添加到事务管理器
	 * @param dataSource
	 * @return org.springframework.jdbc.datasource.DataSourceTransactionManager
	 */
	@Bean(name = "dbTransactionManager")
	public DataSourceTransactionManager transactionManager(DynamicRoutingDataSource dataSource) {
		return new DataSourceTransactionManager(dataSource);
	}

}